/*
 * Declarations shared between the different parts of the ipcio module
 */

#if defined(__unix__)
	#include <unistd.h>
	#if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200112L)
		#define PLAT_POSIX
	#else
		#error Unsupported Unix platform, _POSIX_VERSION < 200112
	#endif
#elif defined(_WIN32)
	#include <sdkddkver.h>
	#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= _WIN32_WINNT_WINXP)
		#define PLAT_WINDOWS
	#else
		#error Unsupported Windows platform, _WIN32_WINNT < _WIN32_WINNT_WINXP
	#endif
#else
	#error Unsupported platform
#endif

#include "Python.h"

#if defined(PLAT_WINDOWS)

	#define WIN32_LEAN_AND_MEAN
	//#define FD_SETSIZE 128
	#include <winsock2.h>
	#include <ws2tcpip.h>
//	typedef struct sockaddr Py_sockaddr_t;
	typedef SOCKET Py_sockfd_t;
	#define INVALID_SOCKFD INVALID_SOCKET
	#define BLOCKING_ERROR (WSAGetLastError() == WSAEWOULDBLOCK)
	#define SHUT_RD SD_RECEIVE
	#define SHUT_WR SD_SEND
	#define SHUT_RDWR SD_BOTH
	#define HAVE_INET

#elif defined(PLAT_POSIX)

	#include <sys/socket.h>
//	typedef struct sockaddr Py_sockaddr_t
	typedef int Py_sockfd_t;
	#define INVALID_SOCKFD -1
	#define BLOCKING_ERROR (errno == EWOULDBLOCK || errno == EAGAIN)
	#define HAVE_INET
	#define HAVE_UNIX

#endif

/* Concrete classes */

extern PyTypeObject PySockAddr_Type;
extern PyTypeObject PySocketIO_Type;
extern PyTypeObject PyPassiveSocket_Type;
extern PyTypeObject PyBufferedSequential_Type;

/* PySocketIO interface */

typedef struct {
	PyObject_HEAD
	Py_sockfd_t fd;
	unsigned short readable   : 1;
	unsigned short writable   : 1;
	unsigned short closefd    : 1;
//	unsigned short listening  : 1;
	unsigned short connecting : 1;
	unsigned short urgentmode : 1;
    PyObject *weakref;
    PyObject *dict;
} PySocketIOObject;

#define PySocketIO_Check(op) (PyObject_TypeCheck((op), &PySocketIO_Type) || \
							  PyObject_TypeCheck((op), &PyPassiveSocket_Type))

#define PySocketIO_IsClosed(op) ((op)->fd == INVALID_SOCKFD)
#define PySocketIO_IsReadable(op) ((op)->readable && !(obj)->listening && !(obj)->connecting)
#define PySocketIO_IsWritable(op) ((op)->writable && !(obj)->listening && !(obj)->connecting)

/* PySockAddr interface */

/* This struct includes a uintptr_t member, which should force at least 8 byte
   alignment on 64 bit platforms and at least 4 byte alignment on 32 bit
   platforms, achieving the same alignment as sockaddr_storage but with
   variable size. */
typedef struct {
	PyObject_VAR_HEAD
	Py_uintptr_t ob_item[1];
//	char ob_item[1];
} PySockAddrObject;

#define PySockAddr_Check(op) PyObject_TypeCheck(op, &PySockAddr_Type)
#define PySockAddr_CheckExact(op) (Py_TYPE(op) == &PySockAddr_Type)

PyObject *PySockAddr_New(int family);
PyObject *PySockAddr_FromAddr(const void *addr, Py_ssize_t size);
PyObject *PySockAddr_FromName(const char *name, const char *mode, int flags);
PyObject *PySockAddr_ListAllFromName(const char *name, const char *mode, int flags);
PyObject *PySockAddr_GetName(PyObject *addrobj, int flags);

#define PySockAddr_GET_SIZE(op) Py_SIZE(op)
#define PySockAddr_GET_ADDR(op) ((struct sockaddr *) ((PySockAddrObject*)(op))->ob_item)
//#define PySockAddr_GET_ADDRT(op, ty) ((ty*) ((PySockAddrObject*)(op))->ob_item)
//#define PySockAddr_GET_SADDR(op) PySockAddr_GET_ADDRT(op, struct sockaddr)

/*
 * string manipulation functions for Py_UNICODE strings with ascii operands
 */

#ifdef HAVE_UINT64_T
#define BITVEC_T PY_UINT64_T
#else
#define BITVEC_T PY_UINT32_T
#endif

#define BITVEC_LEN (sizeof(BITVEC_T)*8)
#define BITVEC_IDX(c) ((unsigned)(c) / BITVEC_LEN)
#define BITVEC_BIT(c) ((BITVEC_T) 1 << ((unsigned)(c) % BITVEC_LEN))

#define ASCII_MAX 127U
#define ASCII_UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define ASCII_LOWER "abcdefghijklmnopqrstuvwxyz"
#define ASCII_DIGITS "0123456789"
#define ASCII_HEXDIGITS "0123456789ABCDEFabcdef"
#define ASCII_ALPHA ASCII_UPPER ASCII_LOWER
#define ASCII_ALNUM ASCII_ALPHA ASCII_DIGITS

Py_LOCAL(Py_ssize_t)
Py_unispn(const Py_UNICODE *uni, Py_ssize_t len, const char *set)
{
	BITVEC_T vec[(ASCII_MAX + 1) / BITVEC_LEN] = {0};
	BITVEC_T bit;
	unsigned idx;
	Py_ssize_t cnt;

	if (len == 0)
		return 0;
	for (; *set != 0; set++)
	{
		assert((unsigned) *set <= ASCII_MAX);
		idx = BITVEC_IDX(*set);
		bit = BITVEC_BIT(*set);
		vec[idx] |= bit;
	}
	for (cnt = 0; cnt < len; cnt++)
	{
		if (uni[cnt] > ASCII_MAX)
			break;
		idx = BITVEC_IDX(uni[cnt]);
		bit = BITVEC_BIT(uni[cnt]);
		if ((vec[idx] & bit) == 0)
			break;
	}
	return cnt;
}

Py_LOCAL(Py_ssize_t)
Py_unicspn(const Py_UNICODE *uni, Py_ssize_t len, const char *set)
{
	BITVEC_T vec[(ASCII_MAX + 1) / BITVEC_LEN] = {0};
	BITVEC_T bit;
	unsigned idx;
	Py_ssize_t cnt;

	if (len == 0)
		return 0;
	for (; *set != 0; set++)
	{
		assert((unsigned) *set <= ASCII_MAX);
		idx = BITVEC_IDX(*set);
		bit = BITVEC_BIT(*set);
		vec[idx] |= bit;
	}
	for (cnt = 0; cnt < len; cnt++)
	{
		if (uni[cnt] > ASCII_MAX)
			continue;
		idx = BITVEC_IDX(uni[cnt]);
		bit = BITVEC_BIT(uni[cnt]);
		if ((vec[idx] & bit) != 0)
			break;
	}
	return cnt;
}

Py_LOCAL_INLINE(Py_UNICODE *)
Py_unipbrk(const Py_UNICODE *uni, Py_ssize_t len, const char *set)
{
	Py_ssize_t cnt = Py_wcscspn(uni, len, set);

	if (cnt < len)
		return (Py_UNICODE *) (uni + cnt);
	else
		return NULL;
}

Py_LOCAL_INLINE(Py_UNICODE *)
Py_unichr(const Py_UNICODE *uni, Py_ssize_t len, const char ch)
{
	Py_ssize_t cnt;

	for (cnt = 0; cnt < len; cnt++)
		if (uni[cnt] == ch)
			return (Py_UNICODE *) (uni + cnt);
	return NULL;
}
